package com.asianrapid.core.utils;

import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @Auther: 迟家鑫
 * @Date: 2018/6/19 13:42
 * @Description:数学工具类
 */
public final class MathUtils {

    private static final String NUMBER_PATTERN = "^[0-9]+(.[0-9]{0,2})?$";// 判断小数点后二位的数字的正则表达式

    /**
     * BigDecimal除法，最大标度29。除不尽保留29位小数
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return　BigDecimal1 / BigDecimal2
     */
    public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor) {
        if (dividend == null || divisor == null) {
            throw new RuntimeException("不能除 NULL值。");
        }
        BigDecimal result = null;

        try {
            result = dividend.divide(divisor);
        } catch (ArithmeticException ex) {
            result = dividend.divide(divisor, 29, BigDecimal.ROUND_HALF_EVEN);
        }

        return result;
    }

    /**
     * 判断字符串是不是数字，数字最多保留两位小数
     *
     * @param strNUmber 数字类型的字符串
     * @return　boolean 符合条件返回true，不符合条件返回false
     */
    public static boolean isDecimalNumber(String strNUmber) {

        boolean isDecimal = false;

        if (StringUtils.isEmpty(strNUmber)) {
            isDecimal = false;
        } else {
            if (strNUmber.lastIndexOf(".") == strNUmber.length() - 1) {
                isDecimal = false;
            } else {
                isDecimal = match(NUMBER_PATTERN, strNUmber);
            }
        }

        return isDecimal;
    }

    private static boolean match(String pattern, String str) {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(str);
        return m.find();
    }

    /**
     * <p>
     * 得到两个 <code>double</code>值中最大的一个.
     * </p>
     *
     * @param a 值 1
     * @param b 值 2
     * @return 最大的值
     */
    public static float getMax(float a, float b) {
        if (Float.isNaN(a)) {
            return b;
        } else if (Float.isNaN(b)) {
            return a;
        } else {
            return Math.max(a, b);
        }
    }

    /**
     * <p>
     * 得到数组中最大的一个.
     * </p>
     *
     * @param array 数组不能为null，也不能为空。
     * @return 得到数组中最大的一个.
     * @throws IllegalArgumentException 如果 <code>数组</code> 是 <code>null</code>
     * @throws IllegalArgumentException 如果 <code>数组</code>是空
     */
    public static float getMax(float[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        float max = array[0];
        for (int j = 1; j < array.length; j++) {
            max = getMax(array[j], max);
        }

        return max;
    }

    /**
     * <p>
     * 得到数组中最大的一个.
     * </p>
     *
     * @param array 数组不能为null，也不能为空。
     * @return 得到数组中最大的一个.
     * @throws IllegalArgumentException 如果 <code>数组</code> 是 <code>null</code>
     * @throws IllegalArgumentException 如果 <code>数组</code>是空
     */
    public static double getMax(double[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("The Array must not be null");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("Array cannot be empty.");
        }

        // Finds and returns max
        double max = array[0];
        for (int j = 1; j < array.length; j++) {
            max = getMax(array[j], max);
        }

        return max;
    }

    /**
     * <p>
     * 得到两个 <code>double</code>值中最大的一个.
     * </p>
     *
     * @param a 值 1
     * @param b 值 2
     * @return 最大的值
     */
    public static double getMax(double a, double b) {
        if (Double.isNaN(a)) {
            return b;
        } else if (Double.isNaN(b)) {
            return a;
        } else {
            return Math.max(a, b);
        }
    }

    /**
     * <p>
     * 得到两个float中最小的一个。
     * </p>
     *
     * @param a 值 1
     * @param b 值 2
     * @return 值最小的
     */
    public static float getMin(float a, float b) {
        if (Float.isNaN(a)) {
            return b;
        } else if (Float.isNaN(b)) {
            return a;
        } else {
            return Math.min(a, b);
        }
    }

    /**
     * <p>
     * 返回数组中最小的数值。
     * </p>
     *
     * @param array 数组不能为null，也不能为空。
     * @return 数组里面最小的float
     * @throws IllegalArgumentException 如果<code>数组</code>是<code>null</code>
     * @throws IllegalArgumentException 如果<code>数组</code>是空
     */
    public static float getMin(float[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("数组不能为null。");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("数组不能为空。");
        }

        // Finds and returns min
        float min = array[0];
        for (int i = 1; i < array.length; i++) {
            min = getMin(array[i], min);
        }

        return min;
    }

    /**
     * <p>
     * 返回数组中最小的double。
     * </p>
     *
     * @param array 数组不能为null，也不能为空。
     * @return 数组里面最小的double
     * @throws IllegalArgumentException 如果<code>数组</code>是<code>null</code>
     * @throws IllegalArgumentException 如果<code>数组</code>是空
     */
    public static double getMin(double[] array) {
        // Validates input
        if (array == null) {
            throw new IllegalArgumentException("数组不能为null。");
        } else if (array.length == 0) {
            throw new IllegalArgumentException("数组不能为空。");
        }
        // Finds and returns min
        double min = array[0];
        for (int i = 1; i < array.length; i++) {
            min = getMin(array[i], min);
        }
        return min;
    }

    /**
     * <p>
     * 得到两个double中最小的一个。
     * </p>
     *
     * @param a 值 1
     * @param b 值 2
     * @return double值最小的
     */
    public static double getMin(double a, double b) {
        if (Double.isNaN(a)) {
            return b;
        } else if (Double.isNaN(b)) {
            return a;
        } else {
            return Math.min(a, b);
        }
    }

    /**
     * 返回两个double的商 first除以second。
     *
     * @param first  第一个double
     * @param second 第二个double
     * @return double
     */
    public static double divideDouble(double first, double second) {
        BigDecimal b1 = new BigDecimal(first);
        BigDecimal b2 = new BigDecimal(second);
        return b1.divide(b2).doubleValue();
    }

    /**
     * 返回两个double的乘积 first*second。
     *
     * @param first  第一个double
     * @param second 第二个double
     * @return double
     */
    public static double multiplyDouble(double first, double second) {
        BigDecimal b1 = new BigDecimal(first);
        BigDecimal b2 = new BigDecimal(second);
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 返回两个double的差值 first-second。
     *
     * @param first  第一个double
     * @param second 第二个double
     * @return double
     */
    public static double subtractDouble(double first, double second) {
        BigDecimal b1 = new BigDecimal(first);
        BigDecimal b2 = new BigDecimal(second);
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 返回两个double的和值 first+second。
     *
     * @param first  第一个double
     * @param second 第二个double
     * @return double
     */
    public static double sumDouble(double first, double second) {
        BigDecimal b1 = new BigDecimal(first);
        BigDecimal b2 = new BigDecimal(second);
        return b1.add(b2).doubleValue();
    }

    /**
     * 格式化double指定位数小数。例如将11.123格式化为11.1。
     *
     * @param value    原double数字。
     * @param decimals 小数位数。
     * @return 格式化后的double，注意为硬格式化不存在四舍五入。
     */
    public static String formatDouble(double value, int decimals) {
        String doubleStr = "" + value;
        int index = doubleStr.indexOf(".") != -1 ? doubleStr.indexOf(".")
                : doubleStr.indexOf(",");
        // Decimal point can not be found...
        if (index == -1)
            return doubleStr;
        // Truncate all decimals
        if (decimals == 0) {
            return doubleStr.substring(0, index);
        }
        int len = index + decimals + 1;
        if (len >= doubleStr.length())
            len = doubleStr.length();
        double d = Double.parseDouble(doubleStr.substring(0, len));
        return String.valueOf(d);
    }

    /**
     * 生成一个指定位数的随机数，并将其转换为字符串作为函数的返回值。
     *
     * @param numberLength 随机数的位数。
     * @return String 注意随机数可能以0开头。
     */
    public static String randomNumber(int numberLength) {
        // 记录生成的每一位随机数
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < numberLength; i++) {
            // 每次生成一位,随机生成一个0-10之间的随机数,不含10。
            Double ranDouble = Math.floor(Math.random() * 10);
            sb.append(ranDouble.intValue());
        }
        return sb.toString();
    }

    /**
     * 功能：生成一个在最大数和最小数之间的随机数。会出现最小数，但不会出现最大数。
     *
     * @param minNum 最小数
     * @param maxNum 最大数
     * @return int
     */
    public static int randomNumber(int minNum, int maxNum) {
        if (maxNum <= minNum) {
            throw new RuntimeException("maxNum必须大于minNum!");
        }
        // 计算出来差值
        int subtract = maxNum - minNum;
        Double ranDouble = Math.floor(Math.random() * subtract);
        return ranDouble.intValue() + minNum;
    }

    /**
     * 功能：生成一个在最大数和最小数之间的随机数。会出现最小数，但不会出现最大数。<br/>
     * 但不随机notin数组中指定的数字， 如果可随机的范围较小，可能会一直随机不到，或者随机的很慢。
     *
     * @param minNum 最小数
     * @param maxNum 最大数
     * @param notin  不随机数组这些数字
     * @return int
     */
    public static int randomNumber(int minNum, int maxNum, Integer[] notin) {
        if (notin.length >= (maxNum - minNum)) {
            throw new RuntimeException("notin数组的元素已经把可以随机的都排除了，无法得到随机数!");
        }
        while (true) {
            int num = randomNumber(minNum, maxNum);
            if (!CollectionUtils.arrayContain(notin, num)) {
                return num;
            }
        }
    }

}